﻿#ifndef XQPYTHON_H
#define XQPYTHON_H
#include<QObject>
#include<QVariantList>
typedef struct _object PyObject;
typedef struct _ts PyThreadState;
/*C++调用python
{python测试代码
	XQPython::setPythonHome("C:\\Users\\用户\\anaconda3");//设置python解释器地址->anaconda目录
	XQPython py;//实例化
	py.addCodePath("../Src/Python");//添加py代码所在的目录 相对路径时已生成的exe目录开始
	py.setPythonFileName("mytest");//设置py文件(只要名字不要后缀)
	py.runFunc("函数名称");//运行函数
	auto value=py.getReturnValue();//获取上次运行的返回值(支持 布尔-浮点数-整形-列表-元组-字符串)这些类型支持嵌套
}
#cmake配置
#第Python3.1加入
set(PythonPath "C:/Users/用户/anaconda3")#anaconda的安装目录，一般只需要改这一个
set(PY_INCLUDE_DIR ${PythonPath}/include)
set(PY_LIB_DIR ${PythonPath}/libs)
include_directories(${PY_INCLUDE_DIR})#加入头文件
link_directories(${PY_LIB_DIR})#加入静态库
file(GLOB PY_DEPENDENCY_DIR  ${PythonPath}/*.dll ${PythonPath}/DLLs ${PythonPath}/Lib)
*/
class XQPython :public QObject
{
	Q_OBJECT
public:
	XQPython(QObject* parent = nullptr);
	XQPython(const QString& pythonHome,QObject* parent = nullptr);
	~XQPython();
	//返回python解释器地址
	static QString pythonHome();
	//设置python解释器地址
	static void setPythonHome(const QString& pythonHome);
public:
	//添加py代码所在的目录
	void addCodePath(const QString& path);
	//清空py代码所在的目录
	void clearCodePath();
	//设置py文件(只要名字不要后缀)执行后会运行对应的python文件
	bool setPythonFileName(const QString& name);
	//运行函数
	bool runFunc(const QString& funcName,const QVariantList& Args = QVariantList());
	template <typename ...Args>
	bool runFunc(const QString& funcName,Args&& ...args);
	//获取上次运行的返回值
	bool getReturnBool();//布尔
	int getReturnInt();//整形
	qint64 getReturnLongLong();//长整型
	double getReturnDouble();//浮点数
	QString getReturnString();//字符串
	QVariantList getReturnList();//列表
	QVariantList getReturnTuple();//元组
	template <typename Key>
	QMap<Key,QVariant> getReturnDict();//字典暂不支持嵌套字典
	//获取上次运行的返回值(支持 布尔-浮点数-整形-列表-元组-字符串)这些类型支持嵌套
	QVariant getReturnValue();
	bool getReturnValue(const QString& format,void* ptr);
	template <typename ...Args>
	bool getReturnTuple(const QString& format, Args&& ...args);
protected:
	void init();
	void PyDECREF(void*object);
	int PyDictCheck(PyObject* object);
	size_t PyDictSize(PyObject* object);
	PyObject* PyDictKeys(PyObject* object);
	PyObject* PyListGetItem(PyObject* object,size_t nSel);
	PyObject* PyDictGetItem(PyObject* object, PyObject*key);
	QVariant getReturnValue(PyObject* pValue);
protected:
	static QString m_PythonHome;//py解释器的路径地址
	PyThreadState* m_threadState=nullptr;
	PyObject* m_pModule = nullptr;//模型
	PyObject* m_pValue = nullptr;//函数返回值
	PyObject* (*m_PyObject_GetAttrString)(PyObject*, const char*) = nullptr;
	PyObject* (*m_PyObject_CallFunction)(PyObject*, const char*, ...) = nullptr;
	int (*m_PyArg_Parse)(PyObject*, const char*, ...) = nullptr;
	int(*m_PyArg_ParseTuple)(PyObject*, const char*, ...) = nullptr;
};
template<typename ...Args>
inline bool XQPython::runFunc(const QString& funcName,Args && ...args)
{
	QVariantList list = { std::forward<Args>(args)... };
	return runFunc(funcName,static_cast<const QVariantList&>(list));
}

template<typename Key>
inline QMap<Key, QVariant> XQPython::getReturnDict()
{
	QMap<Key, QVariant> dict;
	if (PyDictCheck(m_pValue))
	{//字典处理
		size_t dictSize = PyDictSize(m_pValue);
		PyObject* keys = PyDictKeys(m_pValue);
		for (size_t i = 0; i < dictSize; ++i) {
			PyObject* key = PyListGetItem(keys, i);
			PyObject* value = PyDictGetItem(m_pValue, key);
			dict.insert(getReturnValue(key).value<Key>(), getReturnValue(value));
			PyDECREF(key);
			PyDECREF(value);
		}
		PyDECREF(keys);
	}
	return std::move(dict);
}

template<typename ...Args>
inline bool XQPython::getReturnTuple(const QString& format, Args && ...args)
{
	return m_PyArg_ParseTuple(m_pValue, format.toUtf8().data(), std::forward<Args>(args)...) == 0;
}
#endif

